﻿using DigitalHealth.PACS.ChatWebSocket.Services;
using DigitalHealth.PACS.Model;
using log4net;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Authorization;

namespace DigitalHealth.PACS.ChatWebSocket.Middleware
{
    /// <summary>
    /// 及时通讯WebSocket中间件
    /// </summary>
    public class ChatWebSocketMiddleware
    {
        public IChatServer ChatServer { get; set; }
        public IChatUserServer ChatUserServer { get; set; }
        /// <summary>
        /// .Net Core自带日志对象
        /// </summary>
        private readonly ILogger<ChatWebSocketMiddleware> _logger;

        /// <summary>
        /// Log4net 日志对象
        /// </summary>
        //private readonly ILog Log = LogManager.GetLogger(typeof(ChatWebSocketMiddleware));

        /// <summary>
        /// 对用户WebSocket对象进行键值对管理
        /// </summary>
        public static ConcurrentDictionary<string, WebSocket> _sockets = new ConcurrentDictionary<string, WebSocket>();

        /// <summary>
        /// 请求管道
        /// </summary>
        private readonly RequestDelegate _next;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="next"></param>
        public ChatWebSocketMiddleware(RequestDelegate next, ILogger<ChatWebSocketMiddleware> logger, IChatServer ChatServer, IChatUserServer ChatUserServer)
        {
            _next = next;
            _logger = logger;
            this.ChatServer = ChatServer;
            this.ChatUserServer = ChatUserServer;
        }

        /// <summary>
        /// 网络请求上下文
        /// </summary>
        public static HttpContext _context;

        /// <summary>
        /// 调用函数
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task Invoke(HttpContext context)
        {
            _context = context;
            _logger.LogInformation("开始调用websocket中间件");
            //Log.Debug("开始调用...");
            try
            {
                // 不是WebSocket请求直接返回
                if (!context.WebSockets.IsWebSocketRequest)
                {
                    _logger.LogInformation("不是WebSocket请求略过中间件");
                    await _next.Invoke(context);
                    return;
                }

                CancellationToken ct = context.RequestAborted;
                var currentSocket = await context.WebSockets.AcceptWebSocketAsync();

                // 获取请求人ID
                string socketId = context.Request.Query["userId"].ToString();

                // 当前WebSocket加入缓存
                if (!_sockets.ContainsKey(socketId))
                {
                    currentSocket.GetHashCode();
                    _sockets.TryAdd(socketId, currentSocket);
                }
                else
                {
                    try
                    {//向之前的客户端发送离线通知
                        _sockets.TryRemove(socketId, out WebSocket socket);
                        MsgTemplate msgTemplate = new NoticeMessage() { SenderUser = ChatUserServer.GetChatUsersById(socketId), State = Model.Enumeration.MsgState.已读, ChatTime = DateTime.Now, Content = "OffLine", Id = new Guid(), Type = Model.Enumeration.MessageType.事件 };
                        ChatServerContext chatServerContext = new ChatServerContext() { ChatType = Model.Enumeration.ChatType.通知, ChatMessage = msgTemplate };
                        await SendStringAsync(socket, JsonConvert.SerializeObject(chatServerContext), ct);
                        await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", ct);
                    }
                    catch (Exception)
                    {
                        _sockets.TryRemove(socketId, out WebSocket socket4);
                    }
                    _sockets.TryAdd(socketId, currentSocket);
                }

                //登录取未读消息发送到客户端
                var notReadMsg = ChatServer.GetNotReadChatMessage(socketId);
                foreach (var item in notReadMsg)
                {
                    ChatServerContext chatServerContext= new ChatServerContext() { ChatType = Model.Enumeration.ChatType.私聊, ChatMessage = item };
                    _logger.LogInformation($"发送文本信息：{JsonConvert.SerializeObject(chatServerContext)}");
                    await SendStringAsync(currentSocket, JsonConvert.SerializeObject(chatServerContext), ct);
                    chatServerContext.ChatMessage.State = Model.Enumeration.MsgState.已读;
                    ChatServer.Update(chatServerContext);
                }

                while (true)
                {
                    if (ct.IsCancellationRequested)
                    {
                        break;
                    }
                    // 接收文本信息
                    string response = await ReceiveStringAsync(currentSocket, ct);

                    if (string.IsNullOrEmpty(response))
                    {
                        if (currentSocket.State != WebSocketState.Open)
                        {
                            break;
                        }
                        continue;
                    }

                    _logger.LogInformation($"收到文本信息：{response}");
                    //Log.Debug($"收到文本信息：{response}");

                    var msg = JsonConvert.DeserializeObject<ChatServerContext>(response, new ChatServerContextJsonConverter());

                    var senduser=ChatUserServer.GetChatUsersById(msg.ChatMessage.SenderUser.Id.ToString());
                    if (senduser==null)//发送消息者已删除
                    {
                        _logger.LogInformation($"发送消息者已删除，忽略本条消息");
                        continue;
                    }
                    if (msg.ChatType == Model.Enumeration.ChatType.私聊)
                    {
                        var receiveruser = ChatUserServer.GetChatUsersById(((ChatMessage)msg.ChatMessage).ReceiverUser.Id.ToString());
                        if (receiveruser==null)//接收消息者已删除
                        {
                            _logger.LogInformation($"接收消息者已删除，忽略本条消息");
                            continue;
                        }
                    }
                    
                    ChatServer.Create(msg);

                    //ConcurrentDictionary<string, WebSocket> socketList;
                    IEnumerable<KeyValuePair<string, WebSocket>> socketList = default;

                    if (msg.ChatType == Model.Enumeration.ChatType.私聊)
                    {
                        socketList = _sockets.Where(s => s.Key == ((ChatMessage)msg.ChatMessage).ReceiverUser.Id.ToString());
                    }

                    if (msg.ChatType == Model.Enumeration.ChatType.群聊)
                    {

                        socketList = _sockets.Where(s => ((ChatGroup)((ChatGroupMessage)msg.ChatMessage).ReceiverOrg).users.Select(u => u.Id.ToString()).ToArray().Contains(s.Key));
                    }

                    foreach (var item in socketList)
                    {
                        if (item.Value.State != WebSocketState.Open)
                        {
                            msg.ChatMessage.State = Model.Enumeration.MsgState.未读;
                            ChatServer.Update(msg);
                            continue;
                        }
                        _logger.LogInformation($"发送文本信息：{JsonConvert.SerializeObject(msg)}");
                        await SendStringAsync(item.Value, JsonConvert.SerializeObject(msg), ct);
                        msg.ChatMessage.State = Model.Enumeration.MsgState.已读;
                        ChatServer.Update(msg);
                    }
                }

                // 关闭当前WebSocket
                try
                {
                    await currentSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", ct);
                    _sockets.TryRemove(socketId, out WebSocket socket1);
                }
                catch (Exception)
                {
                    //_sockets.TryRemove(socketId, out WebSocket socket4);
                }
                // 释放当前WebSocket
                currentSocket.Dispose();
            }
            catch (Exception ex)
            {
                
                Console.OutputEncoding = Encoding.UTF8;
                _logger.LogError($"[WebSocket中间件]Invoke方法出错:{ex.Message}");
                //Log.Error($"[WebSocket中间件]Invoke方法出错:{ex.Message}");
            }
        }

        /// <summary>
        /// 发送文本信息
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="data"></param>
        /// <param name="ct"></param>
        /// <returns></returns>
        public Task SendStringAsync(WebSocket socket, string data, CancellationToken ct = default(CancellationToken))
        {
            _logger.LogDebug($"发送文本信息：{data}");
            //Log.Debug($"发送文本信息：{data}");
            try
            {
                var buffer = Encoding.UTF8.GetBytes(data);
                var segment = new ArraySegment<byte>(buffer);
                return socket.SendAsync(segment, WebSocketMessageType.Text, true, ct);
            }
            catch (Exception ex)
            {
                Console.OutputEncoding = Encoding.UTF8;
                _logger.LogError($"[WebSocket中间件]发送文本信息出错:{ex.Message}");
                //Log.Error($"[WebSocket中间件]发送文本信息出错:{ex.Message}");
                return null;
            }
        }

        

        /// <summary>
        /// 接收文本信息
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="ct"></param>
        /// <returns></returns>
        private async Task<string> ReceiveStringAsync(WebSocket socket, CancellationToken ct = default(CancellationToken))
        {
            try
            {
                var buffer = new ArraySegment<byte>(new byte[8192]);

                using (var ms = new MemoryStream())
                {
                    WebSocketReceiveResult result;
                    do
                    {
                        ct.ThrowIfCancellationRequested();

                        result = await socket.ReceiveAsync(buffer, ct);
                        ms.Write(buffer.Array, buffer.Offset, result.Count);
                    }
                    while (!result.EndOfMessage);

                    ms.Seek(0, SeekOrigin.Begin);

                    if (result.MessageType != WebSocketMessageType.Text)
                    {
                        return null;
                    }

                    using (var reader = new StreamReader(ms, Encoding.UTF8))
                    {
                        return await reader.ReadToEndAsync();
                    }
                }
            }
            catch (Exception ex)
            {
                Console.OutputEncoding = Encoding.UTF8;
                _logger.LogError($"[WebSocket中间件]接收文本信息出错:{ex.Message}");
                //Log.Error($"[WebSocket中间件]接收文本信息出错:{ex.Message}");
                return null;
            }
        }
    }
}
